package alogrithm;

import java.util.ArrayList;
import java.util.Stack;

import javax.swing.JOptionPane;

import engine.Edge;
import engine.Vertex;
import gui.View;

public class NearestNeighbor
{
   private int actived = 1;
   private ArrayList<Edge> edges;
   private int nopath = -1;
   private int normal = 0;
   private int numEdges;
   private Stack<Edge> path;
   private Vertex startNode = null;
   private ArrayList<Vertex> vertices;
   private int visited = 2;

   public NearestNeighbor(View view)
   {
      edges = view.getEdges();
      vertices = view.getVertices();
      numEdges = view.getEdges().size();
      startNode = view.getVertices().get(0);
      path = new Stack<Edge>();
      algorithm();

   }

   public NearestNeighbor(View view, Vertex startNode)
   {
      edges = view.getEdges();
      vertices = view.getVertices();
      numEdges = view.getEdges().size();
      this.startNode = startNode;
      path = new Stack<Edge>();
      algorithm();
   }

   public void algorithm()
   {
      nearestPath(startNode);

   }

   // Return the minium weight whos status is 0
   public Edge getMinEdge(Vertex v)
   {
      int i = 0;
      int max = Integer.MAX_VALUE;
      Edge e = null;
      for (i = 0; i < edges.size(); i++)
         if (edges.get(i).isExist(v) && edges.get(i).getWeight() < max
               && edges.get(i).getStatus() == 0)
            /***********************************************/
            if (edges.get(i).isOtherVisited(v) == false
            && v.isExistInVisitedList(edges.get(i)) == false)
            {
               max = edges.get(i).getWeight();
               e = edges.get(i);
            }
      // when return null mean that there are no other path
      return e;
   }

   /**
    * Find the lowest weight of the marked edge
    * 
    * @return the index of the lowest array edges
    */
   public int getMinWeight()
   {
      int max = Integer.MAX_VALUE;
      int index = 0;
      for (int i = 0; i < numEdges; i++)
         // Check if both nodes is already been visited
         if (edges.get(i).getFirstNode().getStatus() == 2
         && edges.get(i).getSecondNode().getStatus() == 2)
         {

         }
         else if ((edges.get(i).getStatus() == 1 && edges.get(i).getWeight() < max)
               && (edges.get(i).getFirstNode().getStatus() == 2 || edges.get(i)
               .getSecondNode().getStatus() == 2))
         {
            index = i;
            max = edges.get(i).getWeight();
         }

      return index;
   }

   public boolean isAllEdgesVisited()
   {
      int i = 0;
      for (i = 0; i < edges.size(); i++)
         if (edges.get(i).getStatus() != 2)
            return false;
      return true;
   }

   public boolean isAllVertexVisited()
   {
      int i = 0;
      for (i = 0; i < vertices.size(); i++)
         if (vertices.get(i).getStatus() != 2)
            return false;
      return true;
   }

   /**
    * Mark any edges that connected the vertex as an active edge
    * 
    * @param vertex
    */
   public void markConnectEdge(Vertex vertex)
   {
      for (int i = 0; i < numEdges; i++)
         // check is the edges status is non active
         if (edges.get(i).getStatus() == 0
         && (edges.get(i).getFirstNode() == vertex || edges.get(i)
         .getSecondNode() == vertex))
            edges.get(i).setStatus(1);
   }

   public void nearestPath(Vertex v)
   {
      Edge e = getMinEdge(v);
      // System.out.println("Edge "+e.getWeight());
      // If no other path it must rollback

      if (startNode.getStatus() == visited)
      {
         if (isAllVertexVisited())
            return;
         else
            rollBack(v);
      }
      else if (startNode.isAllEdgeStatusEqual(nopath))
      {
         System.out.println("Incomplete");
         JOptionPane.showMessageDialog(null,
               "Incomplete weighted graph, it must be a completed cycle.",
               "Error",
               JOptionPane.ERROR_MESSAGE);
         return;
      }

      else if (e == null)
      {
         if (v == startNode)
            if (startNode.isAllEdgeStatusEqual(nopath))
               return;
         rollBack(v);
      }
      /**********************************************************************
       * else if(v.isExistInVisitedList(e)) {
       * System.out.println("Node "+v.getName() +
       * " EDge already EXIST "+e.getWeight()); e.setStatus(actived);
       * nearestPath(v); }
       */
      else
      {
         // get the next node
         Vertex v2 = e.getOther(v);
         v.addVisitedList(e);
         // IF node 2 already visited get the other path
         if (v2.getStatus() == visited)
         {
            /*
             * if(startNode==v) { e.setStatus(nopath);//block from reuse } else
             * { e.setStatus(actived);//********************* }
             */
            e.setStatus(actived);
            nearestPath(v);
         }
         else
         {
            e.setStatus(visited);
            v2.setStatus(visited);

            // add the edge into visit list
            // v2.addVisitedList(e);
            path.push(e);
            nearestPath(v2);
         }

      }

   }

   public void resetPath(int from, int to)
   {
      for (int i = 0; i < edges.size(); i++)
         if (edges.get(i).getStatus() == from)
            edges.get(i).setStatus(to);
   }

   public void rollBack(Vertex v)
   {

      // roll back to to startNode
      if (path.empty())
      {
         if (startNode.isAllEdgeStatusEqual(nopath))
            return;
         else
         {
            resetPath(actived, normal);// any edges have status of one reset
            // to zero
            // Start new path from the start node
            nearestPath(startNode);
         }

      }
      else
      {
         Edge e = path.pop();
         v.setAllEdgeStatusFromTo(actived, normal);

         // v.setAllEdgeStatusFromTo(2, 1);
         if (path.empty())
         {
            e.setStatus(nopath);
            v.setStatus(normal);
            v.resetVisitList();
            nearestPath(e.getOther(v));
         }
         else
         {
            e.setStatus(1);
            v.setStatus(0);
            v.resetVisitList();
            nearestPath(e.getOther(v));
         }
      }
   }
}
